<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">

<head>
  <title>gvn</title>

  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

  <link href="designstyle.css"
        type="text/css" rel="stylesheet" />

<!-- commenting out so the xhtml validator doesn't whine about < and &&;
     the browser should still find the script tag. -->
<script language="JavaScript1.2" type="text/javascript">
<!--
function highlight(name) {
  if (document.getElementsByTagName) {
    tags              = [ 'span', 'div', 'tr', 'td' ];
    for (i in tags) {
      elements        = document.getElementsByTagName(tags[i]);
      if (elements) {
        for (j = 0; j < elements.length; j++) {
          elementName = elements[j].getAttribute("class");
          if (elementName == name) {
            elements[j].style.backgroundColor = "#C0F0C0";
          } else if (elementName && elementName.indexOf("rev") == 0) {
            elements[j].style.backgroundColor = "#FFFFFF";
          }
        }
      }
    }
  }
}
//-->
  </script>
  <script type="text/javascript" src="http://google-code-prettify.googlecode.com/svn/trunk/src/prettify.js"></script>

  <!-- this script auto-generates a toc based on the H2 and H3 headings -->
  <script src="navbar.js" type="text/javascript"></script>

  <!-- this style sheet is for the style of the toc -->

  <link href="toc.css" type="text/css" rel="stylesheet" />

  <!-- this script adds a "back to top" link before each h2 tag -->
  <script type="text/javascript" src="gototop.js"></script>
</head>

<body onload="prettyPrint()">

<h1><a name="My_Project_" />gvn Versions Nicely</h1>

<!-- Status is one of: Draft, Current, Needs Update, Obsolete -->
<p style="text-align:center">

  <strong>Status:</strong> <em>Draft</em> &nbsp;
  <small>(as of 2008-05-14)</small>
</p>

<address>
<ul>
<li>Eric Gillespie

 <!-- the &lt;/&gt; must be adjacent to the tags here, or a space is inserted -->
 &lt;<a
 href="mailto:epg@google.com">epg@google.com</a>&gt;
</li>
</ul>
</address>

<address>
<!-- last modified date can be different to the "Status date." It automatically updates
whenever the file is modified. -->
<i>Modified:</i>
 <!-- this script automatically sets the modified date,you don't need to modify it -->
    <script type="text/javascript">
        <!--
        var lm = new Date(document.lastModified);
        document.write(lm.toDateString());
        //-->
        </script>
</address>

<p><b>Contents</b></p>
<!-- this div expands out to a list of contents based on the H2 and H3 headings.
Believe it! -->
 <div id="nav"  class="nav-2-levels"></div>

<h2>Objective</h2>

<p>Implement Google version control practices and work flow with
Subversion.</p>

<ul>
  <li>code review
    <ul>
      <li>Allow users to maintain multiple labelled changes (in the
      form of a list of modified files) in a single working copy.</li>

      <li>Allow other users to review a change before it is submitted
      to a code line.</li>

      <li>Version changes to the change over time so users can track
      the evolution of a change in response to reviews.  This makes
      pending changes easy to backup.</li>

      <li>Provide a mechanism to verify that a change submitted to a
      code line was reviewed.</li>
    </ul>
  </li>

  <li>presubmit</li>

  <li>improved user experience over svn
    <ul>
      <li>short-hand URLs</li>
      <li>easy track of changes with changebranches</li>
    </ul>
  </li>

  <li>Provide a Python API for people to use instead of p4lib.  This
  will be a mix of modules in the gvn and svn packages; it is as yet
  unclear how much we will wrap as high level gvn interfaces.</li>
</ul>

<p>g4-compatible command-line and p4lib-compatible API are explicit
non-goals.  Instead, gvn provides Subversive solutions to the problems
g4 and p4lib solve.</p>

<h2>Background </h2>

<p><i>Stuff one needs to know to understand this doc: motivating examples,
previous versions and problems, links to related projects/design docs,
etc. You should mention related work outside of Google if applicable. 
Note: this is background; do <b>not</b> write about your design or
ideas to solve problems here.</i></p>

<p>Start out with a paragraph with one or two sentences speaking
broadly about the whys and wherefores of source control, and then how
we turned around and gave all that up with g4 pending changelists.
Then Mondrian came in to version those changes on the side.  Now let's
bring it back under one source control system.</p>

<p>Link to some svn docs, SCM patterns site, ...</p>

<p>Terminology</p>

<p>"source branch" is used in comments and docstrings all over the place,
but it's confusing: to many, "trunk" is not conceptually a branch, and
it sometimes isn't technically.  "code line" is a better term, but
what about "source"?  It's the "source" from which the changebranch is
copied, but it's also the "target" to which the change is eventually
submitted.</p>

<p>Include very brief overview of svn, including copy/modify/merge
model, branches are copies, atomic commits, arbitrary metadata, and so
on.</p>

<h2>Overview</h2>

<p><i>One page high-level overview; put details in the next section and
background in the previous section. Should be understandable by a new
Google engineer <b>not</b> working on the project.</i></p>

<h2>Detailed Design</h2>

<h3>The changebranch</h3>

<p>If you're feeling masochistic, see <a href="#appendixA">Appendix A:
Where We've Been</a>.</p>

<p>TODO(epg): document properties:</p?
<ul>
  <li>revprops
    <ul>
      <li>gvn:approve* (on snapshots)</li>
      <li>gvn:submitted (on snapshots)</li>
      <li>gvn:block-this-commit (on txn)</li>
      <li>gvn:bypass-hooks (on submits)</li>
      <li>gvn:change (on submits)</li>
      <li>gvn:bug (on snapshots and submits)</li>
    </ul>
  </li>
  <li>node props
    <ul>
      <li>gvn:project (on project roots)</li>
      <li>gvn:superusers (on repository root)</li>
    </ul>
  </li>
</ul>

<h4>Where We Are</h4>

<p>A changebranch is an entry in a project's changes directory in the
repository, associated metadata, and a branch.  The default location
for changebranches is <code>//changes</code>, so user basil's change
"foo" is located at <code>//changes/basil/foo</code> , which
contains <code>//changes/basil/foo/bar</code>, the actual branch.
"bar" is the basename of the directory being copied.</p>

<p>Users refer to changebranches with the canonical change name rather
than the path in the repository.  The canonical change name is of the
form "user/change-name@revnum".  The canonical name of basil's "foo"
change is "basil/foo@17".  Short forms "foo", "foo@17", and
"basil/foo" are all valid as well.  <code>gvn</code> assumes the
user's own changes when the "user/" portion is missing, and assumes
the <code>HEAD</code> revision in the absence of "revnum".</p>

<p>Creation of a changebranch is the same operation as later snapshots
(updates to the changebranch); both are simply calls
to <span class="method">gvn.changebranch.ChangeBranch.Branch</span> .
This creates any parent directories of the changebranch on the fly,
meaning changebranch creation is
transparent.  <span class="command">gvn change</span>
and <span class="command">gvn snapshot</span> open the working copy to
the deepest common path of all paths to be
changebranched.  <span class="method">Branch</span> starts a
transaction, deletes the branch (if any) copies the source branch from
the base revision of the deepest common path, and then applies the
user's changes to the branch.  Because working copies almost always
have mixed revisions, the branch may have been copied from a different
revision than what should be used for a
file.  <span class="method">Branch</span> deletes such files from the
branch and copies them with the correct revision.  This behavior is
identical to that of <span class="command">svn cp . URL</span>.</p>

<p><code>gvn change</code> takes the same options as <code>svn</code>
for setting the log message of the snapshot, but when none are
specified and it runs the user's editor, the log message of the
previous snapshot (if any) is loaded into the form, ready to become
the log message of this snapshot.  The user may edit this, or leave it
as is.  <code>gvn submit</code> copies the log message of the last
snapshot to become the log message of the final submitted revision.
See <code>gvn submit</code> (TODO href me) below for future plans.</p>

<div class="future"><b>Future plans:</b> <code>gvn</code> today
maintains the list of changebranched files in
the <code>.gvnstate</code> file, with none of the
journalling <code>svn</code> itself uses for wc operations.
Duplicating that would be pointless; instead we should add
to <code>svn</code> a general mechanism for applications to store
additional metadata in the Subversion metadata area (<code>.svn</code>
directory).</div>

<p>Another disadvantage is that the smallest change to a changebranch
involves sending all the diffs to the repository again.  If the user
wants to change only the change description, or change one file out of
a hundred, the diffs for all hundred files must be transmitted.</p>

<p>gvn uses the list of changed paths on the latest (or specified)
snapshot to determine which files are considered part of the change,
and which action is taken for each.  The problem with this is that
actions implementing the changebranch are conflated with the actions
the user took.  Since the branch itself is always an Add or Replace,
gvn has no way to know when the user is changing a property
(e.g. svn:ignore or svn:mergeinfo) on the deepest common path itself,
nor does it have a way to distinguish Replace files from Modified
files.</p>

<h4>Where We're Going</h4>

<p>We knew from the beginning that using a branch of any kind to
manage pending changes only works for reasonably sized changes.  Some
changes are so large as to be unreviewable.  For example, when
importing or updating a third-party tree, reviewers can only check the
log message, list of files; they're not going to read over the entire
tree.  The same applies to large merges.  In addition to the human
problem, sending these large changes twice (once to the changebranch
and again for the final submit) is an unreasonable burden.  So, we
need a way to create an artifact with certain metadata about the
change without sending the change itself until final submit.</p>

<p>Additionally, we have the problems described in "Where We Are".  We
can solve all these problems with one change to the model:  when
snapshotting a changebranch, store our own copy of the path change
metadata, and only re-branch files as needed.  We store this as a file
called <code>state</code> in the changebranch container (e.g.
<code>//changes/epg/foo/state</code>).  This <code>state</code> file
is a <a href="http://www.json.org/">JSON</a> serialization of the
new <code>gvn.changebranch.State</code> class.</p>

<p>For the class of changes we heuristically decide are "big" (or
maybe the user has to use an option), we just don't create the
<code>branch</code> part of the changebranch; we create
<code>//changes/epg/foo/state</code> but not
<code>//changes/epg/foo/branch</code> .  <code>gvn review</code> can
still show the changed files and how they were changed (and even where
things are being copied from, e.g. <code>svn mv gigantic
src/gigantic</code>).</p>

<p>Now, for changes where we do branch
to <code>//changes/epg/foo/branch</code> , we do a full snapshot only
at changebranch creation.  On subsequent snapshots,
unless <span class="option">--force</span> is specified, we don't
snapshot files unless <span class="method">NeedsSnapshot</span>.
E.g.</p>

<pre>
# wc is @15, Makefile is @16

wc% gvn opened
 M    Makefile
 M    testing/testcommon.py

wc% gvn change -c foo --non-interactive
Sending Makefile
Sending testing/testcommon.py
Changed epg/foo@17.

=&gt; same as today, copied //trunk@15 and applied diffs
   A /changes/epg/foo
   A /changes/epg/foo/state
   A /changes/epg/foo/branch (from:/trunk@15)
   R /changes/epg/foo/branch/Makefile (from:/trunk/Makefile@16)
   M /changes/epg/foo/branch/testing/testcommon.py

   but review looks at the path's action in the State object and
   prints an M not an R.

wc% gvn opened
--- foo
 M    Makefile
 M    testing/testcommon.py

wc% gvn change -c foo --non-interactive -m 'new log message'
Changed epg/foo@18.
</pre>

<p>The state file looks like:</p>

<pre class="prettyprint">
{"base": "trunk", "base_rev": 15, "paths": {
   "Makefile": {
     "action": "M",
     "base_rev": 16
   },
   "testing/testcommon.py": {
     "action": "M"
   }
 }
}
</pre>


<p><code>gvn</code> notices that
nothing <span class="method">NeedsSnapshot</span>, and just tries to
change the <code>svn:log</code> property of the last snapshot.  If
that fails, it commits a change just to <code>state</code>, updating
all the <code>snap</code> entries to the last snap revision.  Hmm, or
maybe it always does that, rather than trying to change the log.</p>

<p>The <span class="type">State</span> objects haven't changed, so we
didn't have to re-send them and <code>gvn review</code> has no
trouble showing the correct information and diffs.  We didn't have to
send any diffs, we're just making a new revision with a
new <code>svn:log</code> .

<pre>
wc% gvn opened
--- foo
*M    Makefile
 M    testing/testcommon.py
wc% gvn change -c foo --non-interactive -m 'new log message'
Sending Makefile
Changed epg/foo@19.

=&gt; notices only Makefile needs snapshot, so does this:
   M /changes/epg/foo/state
   R /changes/epg/foo/branch/Makefile (from:/trunk/Makefile@16)
</pre>

<p><code>gvn</code> only had to send a new delta for Makefile and set
the snap revisions for everything else to 18:</p>

<pre class="prettyprint">
{"base": "trunk", "base_rev": 15, "paths": {
   "Makefile": {
     "action": "M",
     "base_rev": 16
   },
   "testing/testcommon.py": {
     "action": "M",
     "snap_rev": 18
   }
 }
}
</pre>

<p>If some path had been copied:</p>

<pre class="prettyprint">
{"base": "trunk", "base_rev": 15, "paths": {
   "Makefile": {
     "action": "M",
     "base_rev": 16
   },
   "testing/testcommon.py": {
     "action": "M",
     "snap_rev": 18
   },
   "copy-example": {
     "action": "M",
     "copyfrom_path": "/trunk/foo",
     "copyfrom_rev": 14
   }
 }
}
</pre>

<h3>Commands</h3>

<h4>checkout (co)</h4>

<p>Resolve short "URLs" (e.g. <code>//tools</code>) to real URLs based
on project settings and run <code>svn checkout</code> to create a
working copy.</p>

  <div class="future"><b>Future plans:</b> Take an option specifying a
  "sparse checkout spec" specifying exactly what to check out, rather
  than checking out the whole tree.</div>

  <p class="g4-analog"><code>g4 analog: client + sync</code></p>

<h4>diff (di)</h4>

  <p>Display the differences for locally modified paths.</p>

  <p class="g4-analog"><code>g4 analog: diff</code></p>

<h4>change (ch)</h4>

  <p>Add paths to or remove paths from a changebranch, or delete a
  changebranch entirely.</p>

  <p class="g4-analog"><code>g4 analog: change</code></p>

<h4>changes</h4>

  <p>List local changebranches.</p>

  <p class="g4-analog"><code>g4 analog: none?</code></p>

<h4>opened</h4>

  <p>List locally modified files, grouped by changebranch.</p>

  <p class="g4-analog"><code>g4 analog: opened</code></p>

<h4>mail</h4>

  <p>Mail a changebranch review request.</p>

  <p class="g4-analog"><code>g4 analog: mail</code></p>

<h4>review</h4>

  <p>Show the change description and diffs for a change.</p>

  <p class="g4-analog"><code>g4 analog: describe</code></p>

<h4>snapshot</h4>

  <p>Update a changebranch in the repository.</p>

  <p class="g4-analog"><code>g4 analog: none</code></p>

<h4>approve (ack)</h4>

  <p>Mark a changebranch approved, reviewed.</p>

  <div class="future"><b>Future plans:</b> At the moment, approved ==
  reviewed, but we will probably change that.  The big difference
  between the two under g4 is that you can submit without a 'looks
  good' (though you get nag mail) but not without an approval.</div>

  <p class="g4-analog"><code>g4 analog: approve</code></p>

<h4>submit</h4>

  <p>Submit the changes from a changebranch and remove the
  changebranch.</p>

  <div class="future"><b>Future plans:</b>
    <p>Since changebranches are like the private branches provided by
    distributed systems like svk and Mercurial, it is likely that
    users will want to snapshot frequently, with snapshot log messages
    describing the change being snapshotted, rather than growing a
    single change description with each snapshot.  To facilitate
    this, <code>submit</code> will have an option to run the user's
    editor on a form with <code>gvn log</code> of all snapshots
    loaded, for easy massaging into the final change dscription.</p>

    <p>If the user has set the per-project
    <span class="option">run-presubmit</span> option, this will
    run <span class="command">gvn presubmit</span> .  This is the only
    option which must be in the user project file; users themselves
    must make the decision to allow other committers to run arbitrary
    code on their systems.  If an organization wants to make this
    decision for users, it can hack this check out internally.</p>
  </div>

  <p class="g4-analog"><code>g4 analog: submit</code></p>

<h4>update (up)</h4>

  <p>Run <code>svn udpate</code> to update the working copy.</p>

  <p class="g4-analog"><code>g4 analog: sync</code></p>

<h4>rdiff (rdi)</h4>

  <p class="g4-analog"><code>g4 analog: diff2</code></p>

<h4>describe (desc)</h4>

  <p class="g4-analog"><code>g4 analog: describe -s</code></p>

<h4>log</h4>

  <p>Show the history of changes of a path, or a changebranch.</p>

  <div class="future"><b>Future plans:</b> Will show gvn review status
  properties for each change.</div>

  <p class="g4-analog"><code>g4 analog: changes</code></p>

<h4>import</h4>

  <p>Run <code>svn import</code>.</p>

  <div class="future"><b>Future plans:</b>  replace <code>svn-vendor</code>:

  <p><code>import [local-path] //third-party/subversion [tags]</code></p>

  <p>Import local-path (defaults to .) to
  //third-party/subversion/import (creating directories as needed).
  local-path is distinguished from repo path because it can start with
  at most 1 / and the path after that must start with 2.  If tags are
  listed, copy the new import tree to those tags.  Tags starting with
  any number of / except 0 or 2 is an error.  0 means it's treated as
  a path relative to //third-party/subversion, 2 means it's treated as
  an absolute path.  The tag path will be Replaced if it exists.</p>

  <pre>
  cd svn-1.4.3 && gvn import //third-party/subversion 1.4.3
  gvn import svn-1.4.3 //third-party/subversion # no tags
  gvn import svn-1.4.3 //third-party/subversion collab/1.4.3 //foo/1.4.3
  </pre>

  <pre class="prettyprint">
  svn.ra.do_status to get list of files in target tree
  os.walk to get list from source tree
  sort lists
  for i in difflib.Differ().compare(a, b):
    if i[0] == '-':
      remove i[2:]
    elif i[0] == '+':
      add i[2:]
  </pre>

  </div>

<h4>todo</h4>

  <div class="future"><b>Future plans:</b>

  <p><code>g4 todo</code> is basically:</p>

  <pre class="prettyprint">
  changes_options['status'] = 'pending'
  changes_options['long_output'] = True
  pending_changes = p4.changes([], **changes_options)
  for change in pending_changes:
    changelist = g4utils.GoogleChangelistDescription(change.Description())
    if user in changelist.Reviewers():
      yield change
  </pre>

  <p>The equivalent for <code>gvn</code> would be:</p>

  <pre class="prettyprint">
  for user_path in //changes:
    for change in //changes/user-path:
      if user in change.reviewers:
        yield change
  </pre>

  <p><code>p4 changes -s pending</code> is probably very fast, whereas
  we're talking about multiple round trips for the <code>gvn</code>
  equivalent.  So, this is probably too slow, and we'll need to index
  the changebranches.</p>

  <p>Mondrian should already have this index.  So, <code>gvn
  todo</code> could just use this.  If Mondrian has no API, we can add
  one while we're adding Subversion support.</p>

  <p>Actually, we might be able to do this quickly with
  svn_ra_do_status or svn_ra_do_update, if the new svn_depth_t stuff
  will allow us to ask for //changes/*/* .</p>

  </div>

<h3>Subversion Commands</h3>

<p>These commands are pure pass-through to svn, with // paths
translated to full URLs</p>

<ul>
<li>add</li>
<li>blame (praise, annotate, ann)</li>
<li>cat</li>
<li>cleanup</li>
<li>copy (cp)</li>
<li>delete (del, remove, rm)</li>
<li>export</li>
<li>info</li>
<li>list (ls)</li>
<li>lock</li>
<li>merge</li>
<li>mergeinfo</li>
<li>mkdir</li>
<li>move (mv, rename, ren)</li>
<li>propdel (pdel, pd)</li>
<li>propedit (pedit, pe)</li>
<li>propget (pget, pg)</li>
<li>proplist (plist, pl)</li>
<li>propset (pset, ps)</li>
<li>resolved</li>
<li>revert</li>
<li>status (stat, st)</li>
<li>switch (sw)</li>
<li>unlock</li>
</ul>

<h3>Code</h3>

<h4>class <span class="type">gvn.errors.Root</span></h4>
  <div class="associated">Associated classes:
    <ul>
      <li><span class="type">gvn.errors.User</span></li>
      <li><span class="type">gvn.errors.Internal</span></li>
      <li>...</li>
    </ul>
  </div>

  <p><code>gvn.errors.User</code>-derived exceptions represent errors
  that could originate from a user, though of course an application
  may reasonably catch some of these and use it to know something,
  e.g. that a path in the repository does not exist.  These exception
  classes have a <code>code</code> member, which may be used as an
  exit code.</p>

  <p><code>gvn.errors.Internal</code>-derived exceptions represent
  errors caused by callers, perhaps intentionally, e.g. to indicate
  that a string is not a short URL.</p>

<h4>class <span class="type">gvn.config.Config</span></h4>
  <div class="associated">Associated classes:
    <ul>
      <li><span class="type">gvn.config.ProjectConfig</span></li>
    </ul>
  </div>

  <p>Holds user configuration bits such as commands for running an
  editor or showing a diff.  Also holds the <code>apr_hash_t</code>
  of <code>svn_config_t</code> objects used by svn libraries.  Has
  functions for finding and returning <code>ProjectConfig</code>
  objects, which hold user configuration about a project, e.g. the
  project URL.</p>

<h4>class <span class="type">gvn.repository.Repository</span></h4>
  <div class="associated">Associated classes:
    <ul>
      <li><span class="type">gvn.repository.Dirent</span></li>
      <li><span class="type">gvn.repository.Revision</span></li>
      <li><span class="type">gvn.repository.ChangedPath</span></li>
    </ul>
  </div>

  <p>Represents a connection to a repository.  It holds the username
  used to open the connection, the URL, and functions to turn paths
  into URLs, get the head revision, get revisions (<code>svn
  log</code>), and get information about a repository
  path.  <code>Dirent</code> represents information about a path.
  <code>Revision</code> represents a revision,
  with <code>ChangedPath</code> objects for each path changed in that
  revision.</p>

<h4>class <span class="type">gvn.project.Project</span></h4>

  <p>Holds the project meta-data from the repository, i.e. location of
  change branches, <code>gvn mail</code> template, and how many lines
  of unified diff to include in review mails.  Holds
  a <code>Repository</code> object for the under-lying repository.</p>

<h4>class <span class="type">gvn.wc.WorkingCopy</span></h4>

  <p>Represents a working.  Has path manipulation functions,
  svn_wc_status and svn_wc_entry wrappers, wcprop/post-commit
  wrappers, a map of working copy paths to changebranches for all
  changebranched paths and a <code>Project</code> object.  All input
  and output paths are relative to the top of the working copy, except
  for the path manipulation functions, which translate between
  absolute/wc-relative and repository/local paths.</p>

<h4>def <span class="function">gvn.commit.Drive</span></h4>
  <div class="associated">Associated classes:
    <ul>
      <li>gvn.commit.EditorAction</li>
      <li>gvn.commit.OpenOrMkdir</li>
      <li>gvn.commit.Copy</li>
      <li>gvn.commit.Delete</li>
      <li>gvn.wc.Edit</li>
    </ul>
  </div>

  <p>This function drives a commit.  Callers provide a callback which
  returns a <code>gvn.commit.EditorAction</code> for a repository
  path.  <code>Drive</code> calls this object, passing it the commit
  editor baton.  <code>OpenOrMkdir</code> opens a directory if it
  exists or creates it if not; <code>Copy</code> copies a
  path; <code>Delete</code> deletes a path.  <code>gvn.wc.Edit</code>
  sends a local modification to the repository and schedules this
  action with <code>WorkingCopy</code>'s post-commit queue.</p>

<h4>class <span class="type">gvn.changebranch.ChangeBranch</span></h4>

  <p>Represents a changebranch.  This includes all information about
  the change (such as a list
  of <code>gvn.repository.ChangedPath</code>s) and methods for
  updating, deleting, or submitting the change.</p>

<h4>class <span class="type">gvn.cmdline.Command</span></h4>
  <div class="associated">Associated classes:
    <ul>
      <li><span class="type">gvn.cmdline.Option</span></li>
      <li><span class="type">gvn.cmdline.OptionParser</span></li>
      <li><span class="type">gvn.cmdline.Context</span></li>
    </ul>
  </div>

  <p>Represents a gvn command (e.g. <code>gvn submit</code>),
  including options (parsed from the command line
  using <code>Option</code> and <code>OptionParser</code> (a subclass
  of the standard Python <code>optparse.OptionParser</code>).
  <code>Context</code> discovers and holds information about the
  context in which the command executes.  It holds a
  <code>Config</code> object (from <code>gvn.config.Get</code> and
  modified based on command-line options and environment variables),
  a <code>gvn.wc.WorkingCopy</code> object (if available), and
  a <code>gvn.project.Project</code> object thereby discovered.</p>

<h2>Caveats</h2>

<p>It will be better one day.</p>

<h2>Security Considerations</h2>

<p>presubmit allows a user with write access to a directory to execute
arbitrary code on the systems of all users that checkout that
directory and have the presubmit feature enabled.</p>

<h2>Privacy Considerations</h2>

<p>N/A ?</p>

<h2>Standards</h2>

<p>N/A</p>

<h2>Logging Plan </h2>

<p>N/A ?</p>

<h2>Testing Plan</h2>

<p>???  Test coverage is not so good (39%), but improving all the time.</p>

<h2>Monitoring Plan</h2>

<p>N/A</p>

<h2>Internationalization Plan</h2>

<p>...</p>

<h2>Documentation Plan</h2>

<p>The code is well-commented and docstringed.  Minimal user
documentation in the form of the <code>gvn help</code> command is
available.  We also have
a <a href="http://gvn.googlecode.com/svn/trunk/doc/gvn.html">preliminary
manual</a>.</p>

<h2>Work Estimates </h2>

<p>...</p>

<h2><a name="appendixA">Appendix A: Where We've Been</a></h2>

<p>Rewrite to fix "awkward flow of concepts" --dchristian<p>

<p>Subversion has a feature called "changelists", inspired by
Perforce's pending changelists.  They are not quite the same:  a
changelist in Subversion is identified by an arbitrary string chosen
by the user and is local to a working copy; a pending changelist in
Perforce is identified by a globally unique integer in the same series
as submitted changelists ("revisions" in Subversion) and is stored in
the depot (the actual changed files are local to the client).  To use
these, <code>gvn</code> would have to send around (changelist name,
hostname, working copy path) triples.  The versioning and verification
requirements would go unmet.</p>

<p>The most obvious way to satisfy the versioning and verification
requirements is to track the change in the Subversion repository
rather than only in the working copy; i.e. use a branch.  This
addresses the last three requirements at once: any user can review the
change by diffing branches, changes to the change are versioned
because they are commits to the branch, and a tool can diff the branch
to the submitted revision to verify the submitted revision is the same
as a change that was reviewed.  This leaves only the multiple changes
requirement, which is a local issue.</p>

<p>One way to implement a changebranch is to branch from the working
copy URL at its base revision and switch the working copy to that
branch.  This prevents the multiple changes requirement.
Additionally, it is very expensive; it requires a slow crawl across
the entire working copy, not just the changed files.</p>

<p>Instead, <code>gvn</code> can switch only the files contained in
this change to the branch.  This satisfies all requirements, but
introduces more issues, among them:</p>

<ul>
  <li>How do you changebranch a directory?  If the directory has many
  descendants (in the filesystem, not in history), they must all be
  switched, which may be very expensive.  What about when the user
  wants to make a change to the "ui" directory, but the file
  "ui/foo.c" is already involved in an unrelated change?  This
  situation is impossible to implement with this model.</li>

  <li>How do you changebranch adds?  Create the added file in the
  changebranch (a commit), then switch the locally added file to the
  branch?  To submit, switch the file back to the path on the source
  branch?  But it doesn't exist yet, so that won't work.  But somehow
  we'd have to turn the locally modified (or unmodified) state back
  into a local add on the source branch.</li>

  <li>How do you changebranch deletes?  Switching the removed file to
  the changebranch is simple enough, but once <code>gvn</code> commits
  that to the changebranch, the file is gone from the working copy.
  It has to be resurrected somehow in order to submit the deletion to
  the source branch.</li>

  <li>
    <p>How do you manage updates?  Other people will be changing the
    changebranched files.  If the user naively runs <code>svn
    update</code>, these changes will be missed.  Instead the user
    must always run a special <code>gvn</code> command, which must
    update these files itself.  This can't be accomplished by
    switching all files back to the source branch, updating, and
    switching back; at no point are the "update" changes *local*;
    assuming no conflicts, these changes will come in at the switch to
    source branch and then leave at the switch back to the
    changebranch.</p>

    <p>Instead, <code>gvn</code> must perform a merge from the base
    revision to the update-target revision of the file on source
    branch to the local file.  This must be a separate merge for each
    file in the change, as not all updated files are switched!  This
    is, of course, ridiculously expensive.</p>
  </li>

  <li>What happens if there are conflicts after that merge (not an
  infrequent occurrence when updating)?  Only the user can resolve
  them, so <code>gvn</code> must exit.  What about all these merged
  files?  Does it commit what it can?</li>

  <li>Whether it leaves all "updated" files uncommitted or only the
  conflicted ones, how do those "updates" then get committed?
  Scribble out some state in a ".gvn" file?</li>

  <li>What happens when the user edits a previously-unmodified file
  and puts it onto one of these "updated" changebranches?  The file in
  the working copy is up-to-date, but its copy on the changebranch is
  not.  Squirrel away the user's changed version, revert the file,
  switch it to the source branch, write the unmodified contents of the
  file, commit, and restore the user's modified file?  What about when
  the user reverts the changed file, or moves it to a different
  changebranch?  <code>gvn</code> must track the "update" state for
  each file.</li>

  <li>What happens if the user had changes on a changebranched file
  not yet committed to the changebranch and the user runs this
  "update" command?  Commit the user's changes along with the "update"
  changes, removing the ability to specify the log message on the
  snapshot?  Force the user to snapshot first?</li>

  <li>Finally, all the above adds up to fragility.  It's a lot of
  moving parts, lots of edge cases.  It's very difficult to get it all
  right.  As we never finished implementing this model, it seems
  likely to have even more complications we hadn't yet encountered.
  This is all compounded by the working copy's infamous fragility, and
  the daunting spaghetti that is <code>libsvn_wc</code>.</li>
</ul>

<p>One obvious way around all this is to keep a separate, "shadow"
working copy of the changebranch, so the "real" working copy wouldn't
have any switched files at all.  This opens whole new cans of worms.</p>

<p>So, we changed the model: Re-branch at every snapshot.  By
re-branching every time, all the updating problems vanish (along with
a giant pile of code, of which Erik and I were increasingly
distrustful (and we weren't even finished writing it!)).  Since we
don't have any special update requirements, we no longer have to
switch.  That saves us from all that fragility.  Of course, this model
brings with it some of its own problems...</p>

</body>
</html>
